{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Multiline TRL"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This example demonstrates  how to use `skrf`'s NIST-style Multiline calibration (`NISTMultilineTRL`). First a [simple application](#Simple-Multiline-TRL) is presented, followed by a [full simulation](#Compare-calibration's-with-different-combinations-of-lines) to demonstrate the improvements in calibration accuracy vs the number of lines.  All  data is used in the demonstration is generated by skrf, and the code for this is given [at the end of the example](#Simulation-to-Generate-Data)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  Simple Multiline TRL"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import skrf\n",
    "from skrf.media import CPW, Coaxial\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "skrf.stylely()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  Load data into skrf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#load all measurement data into a dictionary\n",
    "data = skrf.read_all_networks('multiline_trl_data/')\n",
    "\n",
    "# pull  out measurements by name into an ordered list\n",
    "measured_names = ['thru','reflect','linep3mm','line2p3mm','line10mm']\n",
    "measured = [data[k]  for k in measured_names]\n",
    "\n",
    "#  switch terms\n",
    "gamma_f,gamma_r = data['gamma_f'],data['gamma_r']\n",
    "\n",
    "# DUT\n",
    "dut_meas= data['DUT']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Simple Multiline TRL"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# define the line lengths in meters (including thru)\n",
    "l = [0, 0.3e-3, 2.3e-3, 10e-3]\n",
    "\n",
    "#Do the calibration\n",
    "cal = skrf.NISTMultilineTRL(\n",
    "    measured = measured,  #Measured standards\n",
    "    Grefls = [-1], #Reflection coefficient of the reflect, -1 for short\n",
    "    l = l,         #Lengths of the lines\n",
    "    er_est = 7,    #Estimate of transmission line effective permittivity\n",
    "    switch_terms = (gamma_f, gamma_r) #Switch terms\n",
    "    )\n",
    "\n",
    "#Correct the DUT using the above calibration\n",
    "corrected = cal.apply_cal(dut_meas)\n",
    "\n",
    "corrected.plot_s_db()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Compare calibration's with different combinations of lines"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we loop through different line combinations to demonstrate the difference in calibration accuracy."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Run NIST Multiline TRL calibation with different combinations of lines\n",
    "\n",
    "#Put through and reflect to their own list ...\n",
    "mtr = measured[:2]\n",
    "\n",
    "#and lines on their own\n",
    "mlines = measured[2:]\n",
    "\n",
    "# define the line lengths in meters\n",
    "line_len = [0.3e-3, 2.3e-3, 10e-3]\n",
    "\n",
    "cals = []\n",
    "duts = []\n",
    "\n",
    "line_combinations = [ [0], [1], [0,1,2]]\n",
    "\n",
    "for used_lines in line_combinations:\n",
    "\n",
    "    m = mtr + [mlines[i] for i in used_lines]\n",
    "\n",
    "    #Add thru length to list of line lengths\n",
    "    l = [0] + [line_len[i] for i in used_lines]\n",
    "\n",
    "    #Do the calibration\n",
    "    cal = skrf.NISTMultilineTRL(\n",
    "        measured = m,  #Measured standards\n",
    "        Grefls = [-1], #Reflection coefficient of the reflect, -1 for short\n",
    "        l = l,         #Lengths of the lines\n",
    "        er_est = 7,    #Estimate of transmission line effective permittivity\n",
    "        switch_terms = (gamma_f, gamma_r) #Switch terms\n",
    "        )\n",
    "\n",
    "    #Correct the DUT using the above calibration\n",
    "    corrected = cal.apply_cal(dut_meas)\n",
    "    corrected.name = 'DUT, lines {}'.format(used_lines)\n",
    "\n",
    "    duts.append(corrected)\n",
    "    cals.append(cal)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  Results and discussion"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Transmission of corrected DUT\n",
    "Plot the corrected DUT with different amount of lines"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "plt.title('DUT S21')\n",
    "for dut in duts:\n",
    "    dut.plot_s_db(m=1, n=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### S11 of corrected DUT with different amount of lines\n",
    "\n",
    "S11 shows bigger changes. \n",
    "\n",
    "* With one line low frequencies are very noisy\n",
    "* With only the medium length line calibration is very inaccurate at frequencies\n",
    "  where phase of the line is multiple of 180 degrees\n",
    "* With three lines calibration accuracy is excellent everywhere"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "plt.title('DUT S11')\n",
    "for dut in duts:\n",
    "    dut.plot_s_db(m=0, n=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Normalized standard deviation of different calibrations\n",
    "\n",
    "This measures the accuracy of the calibration. Lower number means less noise.\n",
    " * TRL calibration with one 90 degrees long line has normalized standard deviation of 1. \n",
    " * With multiple lines normalized standard deviations less than one is possible.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f_ghz = dut.frequency.f_scaled\n",
    "\n",
    "plt.figure()\n",
    "plt.title('Calibration normalized standard deviation')\n",
    "for e, cal in enumerate(cals):\n",
    "    plt.plot(f_ghz, cal.nstd, label='Lines: {}'.format(line_combinations[e]))\n",
    "plt.ylim([0,30])\n",
    "plt.legend(loc='upper right')\n",
    "dut.frequency.labelXAxis()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### Calculate effective permittivity of the transmission lines used in the calibration\n",
    "\n",
    "Is there no existing way to get the real er_eff? Anyway, this is how it can\n",
    "be calculated from the propagation constant. CPW line propagation constant can\n",
    "be approximated as average of substrate and air permittivities, but this is not\n",
    "completely true at low frequencies when the line is lossy\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#define calibration standard media \n",
    "freq = dut.frequency\n",
    "cpw = CPW(freq, z0=50, w=40e-6, s=25e-6, ep_r=12.9,\n",
    "                     t=5e-6, rho=2e-8)\n",
    "\n",
    "#Get the cal with all the lines\n",
    "cal = cals[-1]\n",
    "\n",
    "#Plot the solved effective permittivity of the transmission lines\n",
    "c = 299792458.0\n",
    "real_er_eff = -(cpw.gamma/(2*np.pi*f_ghz*1e9/c))**2\n",
    "\n",
    "plt.figure()\n",
    "plt.title('CPW effective permittivity')\n",
    "plt.plot(f_ghz, cal.er_eff.real, label='Solved er_eff')\n",
    "plt.plot(f_ghz, real_er_eff.real, label='Actual er_eff')\n",
    "plt.legend(loc='upper right')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Depending on the noise there might be some wrong choices for some lines during\n",
    "the propagation constant determination that cause small spikes in the solved\n",
    "effective permittivity. In general they don't matter much, but incorrectly\n",
    "determined propagation constant affects the weighting of the lines and accuracy\n",
    "of the calibration at that frequency. With many lines though even if one line\n",
    "has incorrectly determined propagation constant the effect to the total calibration\n",
    "will be small.\n",
    "\n",
    "Plot the phase of the solved reflection coefficient"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  Is reflect correct?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since we know the ideals in this simulation we can re-define them here, and compare the determined reflect to the actual reflect.  (see below for simulation details)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lines = [cpw.line(l, 'm') for l in line_len]\n",
    "short = cpw.delay_short(10e-6, 'm')\n",
    "\n",
    "actuals = [\n",
    "    cpw.thru(),\n",
    "    skrf.two_port_reflect(short, short),\n",
    "    ]\n",
    "\n",
    "actuals.extend(lines)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "plt.title('Solved and actual reflection coefficient of the reflect standard')\n",
    "cal.apply_cal(measured[1]).plot_s_deg(n=0, m=0)\n",
    "actuals[1].plot_s_deg(n=0, m=0)\n",
    "\n",
    "plt.show(block=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Simulation to Generate Data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is how we made the data used above. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create frequency and Media "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "freq = skrf.F(1,100,401)\n",
    "\n",
    "#CPW media used for DUT and the calibration standards\n",
    "cpw = CPW(freq, z0=50, w=40e-6, s=25e-6, ep_r=12.9,\n",
    "                     t=5e-6, rho=2e-8)\n",
    "\n",
    "#1.0 mm coaxial media for calibration error boxes\n",
    "coax1mm = Coaxial(freq, z0=50, Dint=0.44e-3, Dout=1.0e-3, sigma=1e8)\n",
    "\n",
    "f_ghz = cpw.frequency.f*1e-9"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Make realistic looking error networks.\n",
    "Propagation constant determination is iterative and doesn't work as well\n",
    "when the error networks are randomly generated"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = coax1mm.line(1, 'm', z0=58, name='X', embed=True)\n",
    "Y = coax1mm.line(1.1, 'm', z0=40, name='Y', embed=True)\n",
    "\n",
    "plt.figure()\n",
    "plt.title('Error networks')\n",
    "X.plot_s_db()\n",
    "Y.plot_s_db()\n",
    "\n",
    "#Realistic looking switch terms\n",
    "gamma_f = coax1mm.delay_load(0.2, 21e-3, 'm', z0=60, embed=True)\n",
    "gamma_r = coax1mm.delay_load(0.25, 16e-3, 'm', z0=56, embed=True)\n",
    "\n",
    "plt.figure()\n",
    "plt.title('Switch terms')\n",
    "gamma_f.plot_s_db()\n",
    "gamma_r.plot_s_db()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Generate Ficticous measurements"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Lengths of the lines used in the calibration, units are in meters\n",
    "\n",
    "line_len = [0.3e-3, 2.3e-3, 10e-3]\n",
    "lines = [cpw.line(l, 'm') for l in line_len]\n",
    "\n",
    "#Attenuator with mismatched feed lines\n",
    "dut_feed = cpw.line(1.5e-3, 'm', z0=55, embed=True)\n",
    "dut = dut_feed**cpw.attenuator(-10)**dut_feed\n",
    "\n",
    "#Through and non-ideal short\n",
    "#Real reflection coefficient is solved during the calibration\n",
    "\n",
    "short = cpw.delay_short(10e-6, 'm')\n",
    "\n",
    "actuals = [\n",
    "    cpw.thru(),\n",
    "    skrf.two_port_reflect(short, short),\n",
    "    ]\n",
    "\n",
    "actuals.extend(lines)\n",
    "\n",
    "#Measured\n",
    "measured = [X**k**Y for k in actuals]\n",
    "\n",
    "#Switch termination\n",
    "measured = [skrf.terminate(m, gamma_f, gamma_r) for m in measured]\n",
    "\n",
    "#Add little noise to the measurements\n",
    "for m in measured:\n",
    "    m.add_noise_polar(0.0005, 0.005)\n",
    "\n",
    "names = ['thru','reflect','linep3mm','line2p3mm','line10mm']\n",
    "for k,name in enumerate(names):\n",
    "    measured[k].name=name\n",
    "    \n",
    "\n",
    "#Noiseless DUT so that all the noise will be from the calibration\n",
    "dut_meas = skrf.terminate(X**dut**Y, gamma_f, gamma_r)\n",
    "dut_meas.name = 'DUT'\n",
    "\n",
    "#Put through and reflect to their own list ...\n",
    "mtr = measured[:2]\n",
    "\n",
    "#and lines on their own\n",
    "mlines = measured[2:]\n",
    "\n",
    "# write data to disk\n",
    "write_data = False\n",
    "if write_data:\n",
    "    [k.write_touchstone(dir='multiline_trl_data/') for k in measured]\n",
    "    gamma_f.write_touchstone('multiline_trl_data/gamma_f.s1p')\n",
    "    gamma_r.write_touchstone('multiline_trl_data/gamma_r.s1p')\n",
    "    dut_meas.write_touchstone(dir='multiline_trl_data/')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
